﻿using System;
using System.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using org.alex.edss;
using org.alex.edss.Security;

namespace TcpDownServer
{
    public class Server
    {
        private static EdsServer serv;
        private static string RootPath;

        public static void Run(int Port,string Root)
        {
            UserManager.GetInstance().Init();
            RootPath = Root;
            Logger.FilePath = AppDomain.CurrentDomain.BaseDirectory + "\\log.txt";
            Logger.Mode = int.Parse(ConfigurationManager.AppSettings["mode"]);

            serv = new EdsServer(Port, 100, 500, 4096);
            serv.AppStart += new ApplicationEventHandler(serv_AppStart);
            serv.AppEnd += new ApplicationEventHandler(serv_AppEnd);
            serv.AppError += new ApplicationEventHandler(serv_AppError);
            serv.SessionStart += new SessionEventHandler(serv_SessionStart);
            serv.Start();
        }

        static void serv_SessionStart(Session session, int ReadBytes)
        {
            //throw new NotImplementedException();
            Logger.Info(session.Key + " is join in server");
            DownloadSession ses = new DownloadSession();
            ses.PathCommand.Init(RootPath);
            session.UserData["data"] = ses;
            session.OnReading += new SessionEventHandler(session_OnReading);
            session.ReadComplete += new SessionEventHandler(session_ReadComplete);
            session.SendComplete += new SessionEventHandler(session_SendComplete);
            session.SessionEnd += new SessionEventHandler(session_SessionEnd);
            session.SessionError += new org.alex.edss.ErrorEventHandler(session_SessionError);
            session.Connection.Read();
        }

        static void session_SessionError(object session, Object Args)
        {
            try
            {
                //throw new NotImplementedException();
                if (((Session)session).UserData.ContainsKey("fs"))
                {
                    try
                    {
                        Logger.Debug("a file not close,close it");
                        ((FileStream)((Session)session).UserData["fs"]).Close();
                    }
                    catch { Logger.Debug("The file was close"); }
                }
                ((Session)session).Close();
                if (Args.GetType().Name.Equals(typeof(string).Name))
                {
                    Logger.Error(Args.ToString());
                }
                else
                {
                    Logger.Error(((Exception)Args).Message);
                }
            }
            catch (Exception ex)
            {
                Logger.Error("记录错误日志出错" + ex.Message);
            }
        }

        static void session_SessionEnd(Session session, int ReadBytes)
        {
            //throw new NotImplementedException();
            Logger.Info("Session is desconnecting");
        }

        static void session_SendComplete(Session session, int ReadBytes)
        {
            //throw new NotImplementedException();
            if (session.Key > 0)
            {
                DownloadSession ds = (DownloadSession)session.UserData["data"];
                if (ds.State == DownloadSession.SessionState.GetC1)
                {
                    ds.State = DownloadSession.SessionState.SendS1;
                    session.Connection.Read();
                }
                if (ds.State == DownloadSession.SessionState.GetC2)
                {
                    ds.State = DownloadSession.SessionState.Connected;
                    session.Connection.Read();
                }
                //if (ds.State == DownloadSession.SessionState.StartDown)
                //{
                //    DownFile(session, (DownloadSession)session.UserData["data"]);
                //}
                if (ds.State == DownloadSession.SessionState.ProcessCommand)
                {
                    ds.State = DownloadSession.SessionState.CommandDone;
                    session.Connection.Read();
                }
                if (ds.State == DownloadSession.SessionState.WaitDown)
                {
                    ds.State = DownloadSession.SessionState.WaitRead;
                    session.Connection.Read();
                }
                if (ds.State == DownloadSession.SessionState.ReadOk)
                {
                    try
                    {
                        FileStream fst = (FileStream)session.UserData["fs"];
                        ds.State = DownloadSession.SessionState.SendOK;
                        if (!ds.PathCommand.ReadDone)
                        {
                            IAsyncResult rs = fst.BeginRead(ds.PathCommand.FileBuffer, 0, 4096,
                                        (AsyncCallback)delegate(IAsyncResult Arr)
                                        {
                                            ds.PathCommand.SubDown(Arr, session);
                                        }, fst);
                        }
                        else
                        {
                            ds.State = DownloadSession.SessionState.CommandDone;
                            session.Connection.Read();
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.Debug("86:Server " + ex.Message);
                        session.SetError();
                    }
                }
            }
        }

        static void session_ReadComplete(Session session, int ReadBytes)
        {
            //throw new NotImplementedException();
        }

        static void session_OnReading(Session session, int ReadBytes)
        {
            //throw new NotImplementedException();
            DownloadSession ds = (DownloadSession)session.UserData["data"];
            if (ds.State == DownloadSession.SessionState.Init)
            {
                GetC1(session, ReadBytes, ds);
            }
            if (ds.State == DownloadSession.SessionState.SendS1)
            {
                GetC2(session, ReadBytes, ds);
            }
            if (ds.State == DownloadSession.SessionState.Connected||ds.State==DownloadSession.SessionState.CommandDone)
            {
                ProcessCommand(session, ReadBytes, ds);
            }
            if (ds.State == DownloadSession.SessionState.WaitRead)
            {
                if (ReadBytes == 8)
                {
                    if (session.ReadBuffer[0] == 0x01 && session.ReadBuffer[4] == 0x05)
                    {
                        ds.State = DownloadSession.SessionState.StartDown;
                        DownFile(session, (DownloadSession)session.UserData["data"]);
                    }
                    else
                    {
                        Logger.Error("get error signal");
                        session.Close();
                    }
                }
                else
                {
                    Logger.Error("get error signal");
                    session.Close();
                }
            }
        }

        private static void ProcessCommand(Session session, int ReadBytes, DownloadSession ds)
        {
            ds.Cmd.SetLength(0);
            ds.Cmd.Write(session.ReadBuffer,0,ReadBytes);
            byte[] cmddata = ds.Cmd.ToArray();
            if (ds.PathCommand.CommandReady(cmddata))
            {
                string Key = UserManager.GetInstance().GetKey(ds.QQ);
                ds.Cmd.SetLength(0);
                ds.Cmd.Write(cmddata, 0, cmddata.Length - 2);
                byte[] DecryptoCmd = null;
                byte[] UnDecryptoCmd = ds.Cmd.ToArray();
                try
                {
                    DecryptoCmd = DES.GetTransfer().Decrypto(UnDecryptoCmd, Key);
                }
                catch
                {
                    Logger.Error("Decrypto faild");
                    session.Close();
                }
                string rs = ds.PathCommand.DoCommand(DecryptoCmd);
                if (!rs.StartsWith("0"))
                {
                    
                    ds.State = DownloadSession.SessionState.ProcessCommand;
                    byte[] EncryptoData = DES.GetTransfer().Encrypto(Encoding.UTF8.GetBytes(rs), Key);
                    session.Connection.Write(EncryptoData);
                }
                else
                {
                    if (ds.PathCommand.CanDown)
                    {
                        Logger.Debug("Get a down request :" + ds.PathCommand.DownPath);
                        FileInfo f = new FileInfo(ds.PathCommand.DownPath);
                        byte[] data = BitConverter.GetBytes(f.Length - ds.PathCommand.Pos);
                        ds.State = DownloadSession.SessionState.WaitDown;
                        //Helper.ClearArray(session.ReadBuffer);
                        session.Connection.Write(data);
                    }
                    //DownFile(session, ds);
                }
            }
            else
            {
                session.Connection.Read();
            }
        }


        private static void DownFile(Session session, DownloadSession ds)
        {
            if (ds.PathCommand.CanDown)
            {
                ds.State = DownloadSession.SessionState.StartDown;
                ds.PathCommand.Down(session);
            }
        }

        private static void GetC2(Session session, int ReadBytes, DownloadSession ds)
        {
            if (ReadBytes > 0)
            {
                if (session.ReadBuffer[0] != (byte)ds.Handshake.Sum)
                {
                    Logger.Debug("exit with wrong count sum " + ds.Handshake.S1[0] + "+" + ds.Handshake.S1[1] + "=" + ds.Handshake.Sum);
                    session.Close();
                }
                ds.State = DownloadSession.SessionState.GetC2;
                session.Connection.Write(new byte[1] { (byte)0 });
            }
        }

        private static void GetC1(Session session, int ReadBytes, DownloadSession ds)
        {
            ds.Link.Write(session.ReadBuffer, 0, ReadBytes);
            if (ReadBytes < ds.Handshake.C1_Len)
            {
                session.Connection.Read();
            }
            else
            {
                ds.Handshake.C1 = ds.Link.ToArray();
                if (ds.Handshake.C1[0] != 0x01)
                {
                    Logger.Info("Exit with wrong version id :" + ds.Handshake.C1[0]);
                    session.Close();
                }
                else
                {
                    bool start = false;
                    for (int i = 1; i < ds.Handshake.C1_Len; i++)
                    {
                        if (!start)
                        {
                            if (ds.Handshake.C1[i] != 0)
                            {
                                start = true;
                            }
                        }
                        if (start)
                        {
                            ds.QQ += ds.Handshake.C1[i];
                        }
                    }
                    ds.State = DownloadSession.SessionState.GetC1;
                    //需要加入认证QQ号码的代码。
                    if (UserManager.GetInstance().CheckQQ(ds.QQ))
                    {
                        session.Connection.Write(ds.Handshake.S1);
                    }
                    else
                    {
                        session.Close();
                    }
                }
            }
        }

        static void serv_AppError(string SystemMessage)
        {
            //throw new NotImplementedException();
            Logger.Error(SystemMessage);
        }

        static void serv_AppEnd(string SystemMessage)
        {
            //throw new NotImplementedException();
            Logger.Info("Server is closing");
        }

        static void serv_AppStart(string SystemMessage)
        {
            //throw new NotImplementedException();
            Logger.Info("Server Start at this time");
        }
    }
}
